﻿using CefSharp;
using CefSharp.WinForms;
using Chen.CefWebKit.Handler;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Chen.CefWebKit
{
    public partial class CefWebBrowser : Form
    {
        public bool IsLoading = false;

        ChromiumWebBrowser wb;
        string HtmlCode;
        string Cachepath = "";
        NewClient nc;
        RenderProcessMessageHandler RenderProcessMessage;
        Action DoHide;
        public CefWebBrowser(string CachePathName="Cache")
        {
            InitializeComponent();

            if (!Cef.IsInitialized)
            {
                var setting = new CefSharp.CefSettings();
                setting.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36";
                setting.LogSeverity = LogSeverity.Disable;
                Cef.Initialize(setting);
            }

            var requestContextSettings = new RequestContextSettings { CachePath = CachePathName };
            var requestContext = new RequestContext(requestContextSettings);
            wb = new ChromiumWebBrowser("");
            wb.RequestContext = requestContext;
            BrwPanel.Controls.Add(wb);
            //while (!wb.IsBrowserInitialized) { }
            //Cef.GetGlobalCookieManager().SetStoragePath(@".\" + CachePathName + @"\", true);
            HtmlCode = "";
            nc = new NewClient(Directory.GetCurrentDirectory() + @"\" + CachePathName + @"\Cookies");

            RenderProcessMessage = new RenderProcessMessageHandler();
            wb.RenderProcessMessageHandler = RenderProcessMessage;
            wb.LoadingStateChanged += Wb_LoadingStateChanged;
        }

        private void Wb_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
        {
            IsLoading = e.IsLoading;
        }

        /// <summary>
        /// 打开浏览器窗口
        /// </summary>
        public void OpenWindow()
        {
            new System.Threading.Thread((System.Threading.ThreadStart)delegate
            {
                try
                {
                    Application.Run(this);
                }
                catch
                {

                }
            }).Start();
        }
        /// <summary>
        /// 隐藏窗口
        /// </summary>
        public void HideWindow()
        {
            this.Invoke(new Action(()=> {
                this.Visible = false;
                this.Hide();
            }));
        }
        public void CloseCef()
        {
            this.Invoke(new Action(() => {
                this.Close();
                wb.Dispose();
            }));
            //Cef.Shutdown();
        }
        private void CefWebBrowser_Load(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// 根据URL获取渲染后的HTML代码
        /// </summary>
        /// <param name="url">链接</param>
        /// <returns></returns>
        public String GetHtmlByUrl(string url)
        {
            Task Tload = LoadPageAsync(wb, url);
            Tload.Wait();
            Task<string> Thtml = wb.GetBrowser().MainFrame.GetSourceAsync();
            Thtml.Wait();
            HtmlCode = Thtml.Result;
            return HtmlCode;
        }
        /// <summary>
        /// 获得当前页面的HTML代码,确认在获取前读取过页面
        /// </summary>
        /// <returns></returns>
        public String GetHtml()
        {
            string Script = JavaScriptLibrary.QuerySelector("body");
            string result = RunScript(Script);
            return result;
            //Task<string> Thtml = wb.GetBrowser().MainFrame.GetSourceAsync();
            //Thtml.Wait();
            //HtmlCode = Thtml.Result;
            //return HtmlCode;
        }
        /// <summary>
        /// 通过Query获得当前页面
        /// </summary>
        /// <param name="Selector"></param>
        /// <returns></returns>
        public string GetHtmlByQuery(string Selector)
        {
            string Script = JavaScriptLibrary.QuerySelector(Selector);
            string result = RunScript(Script);
            return result;
        }
        /// <summary>
        /// 获得未渲染的当前页面源码
        /// </summary>
        /// <returns></returns>
        public string GetHtmlSource()
        {
            var _t = wb.GetSourceAsync();
            _t.Wait();
            return _t.Result;
        }
        /// <summary>
        /// 设置Cookie
        /// </summary>
        /// <param name="name">名称</param>
        /// <param name="value">对应值</param>
        /// <param name="domain">所在域名</param>
        public void SetCookie(string name, string value, string domain, string path = "/")
        {
            string script =
                "seconds = 0;" +
                "var expires = \"\";" +
                "if (seconds != 0 ) {" +
                "var date = new Date();" +
                "date.setTime(date.getTime()+(seconds*1000));" +
                "expires = \"; expires = \"+date.toGMTString();}" +
                "document.cookie ='" + name + " = escape(" + value + "); path =" + path + "; domain =" + domain + "'";
            Task<JavascriptResponse> Tjs = wb.EvaluateScriptAsync(script);
            Tjs.Wait();
            string a = Tjs.Result.Result.ToString();
        }
        /// <summary>
        /// 设置Cookie
        /// </summary>
        /// <param name="cookies"></param>
        public void SetCookie(System.Net.Cookie[] cookies)
        {
            foreach (System.Net.Cookie i in cookies)
            {
                SetCookie(i.Name, i.Value, i.Domain);
            }
        }
        /// <summary>
        /// 获取当前页面Cookie
        /// </summary>
        /// <returns></returns>
        public string GetCookie()
        {
            string script = "document.cookie";
            Task<JavascriptResponse> Tjs = wb.EvaluateScriptAsync(script);
            Tjs.Wait();
            string Result = Tjs.Result.Result.ToString();
            return Result;
        }
        /// <summary>
        /// 清除所有Cookie
        /// </summary>
        public void ClearAllCookie()
        {
            //nc.uc.ClearAllCookie();
            Cef.GetGlobalCookieManager().DeleteCookiesAsync().Wait();
            //string script = @"var keys=document.cookie.match(/[^ =;]+(?=\=)/g);" +
            //                "if (keys) {"+
            //                "for (var i =  keys.length; i--;)"+
            //                "document.cookie=keys[i]+'=0;expires=' + new Date( 0).toUTCString()"+
            //                "} ";
            //RunScript(script);
        }
        /// <summary>
        /// 浏览器打开页面
        /// </summary>
        /// <param name="url">链接</param>
        public void LoadUrl(string url)
        {
            wb.Load(url);
            //Task t = LoadPageAsync(wb, url);
            //t.Wait();
        }
        /// <summary>
        /// 访问一个连接，并等待渲染完成
        /// </summary>
        /// <param name="url">网址</param>
        /// <param name="querySelecot">判断渲染完成的Query选择器标签</param>
        /// <param name="Timeout">超时时间</param>
        /// <param name="maxRetry">最大重试次数</param>
        /// <returns>是否渲染完成</returns>
        public bool LoadAndWaitRender(string url,string querySelector,int Timeout=5000,int maxRetry = 1,int tryCount=0)
        {
            ClearWebPage(50);
            LoadUrl(url);
            int Retry = 0;
            bool isSuccess = false;
            while(!isSuccess && Retry<= maxRetry)
            {
                isSuccess = WaitRenderOver(querySelector,new CancellationTokenSource(Timeout));
                if (!isSuccess)
                {
                    Retry++;
                    LoadUrl(url);
                }
            }
            return isSuccess;
        }
        /// <summary>
        /// 访问一个连接并等待一定时间
        /// </summary>
        /// <param name="url"></param>
        /// <param name="waitTime"></param>
        public void LoadAndWait(string url,int waitTime)
        {
            LoadUrl(url);
            Thread.Sleep(waitTime);
        }
        /// <summary>
        /// 释放页面连接（每次用完页面后请务必释放页面,以防止通过标签判断页面是否渲染失效）
        /// </summary>
        /// <param name="waitTime"></param>
        public void ClearWebPage(int waitTime)
        {
            LoadAndWait("about:blank", waitTime);
        }
        /// <summary>
        /// 判断指定标签对象 是否存在页面上
        /// </summary>
        /// <param name="querySelector">对象选择器</param>
        /// <returns></returns>
        public bool HasNode(string querySelector)
        {
            bool result = false;
            string _html = GetHtmlByQuery(querySelector);
            if (_html != null)
            {
                result = true;
            }
            return result;
        }
        /// <summary>
        /// 通过判断指定标签对象，等待页面渲染完成
        /// </summary>
        /// <param name="querySelector"></param>
        /// <returns></returns>
        public bool WaitRenderOver(string querySelector,CancellationTokenSource CancelToken)
        {
            bool result = false;
            while (!result && !CancelToken.IsCancellationRequested)
            {
                result = HasNode(querySelector);
                Thread.Sleep(500);
            }
            return result;
        }
        /// <summary>
        /// 渲染页面
        /// </summary>
        /// <param name="Source">Html代码</param>
        /// <param name="url">作为页面的URL地址</param>
        public void LoadSource(string Source, string url = "")
        {
            Task t = LoadHtmlAsync(wb, Source, url);
            t.Wait();
        }
        /// <summary>
        /// 运行JS代码
        /// </summary>
        /// <param name="script">JS代码</param>
        /// <returns></returns>
        public string RunScript(string script)
        {
            if (RenderProcessMessage.CanScript)
            {
                Task<JavascriptResponse> Tjs = wb.EvaluateScriptAsync(script);
                Tjs.Wait();
                if (Tjs.Result.Result != null)
                {
                    string a = Tjs.Result.Result.ToString();
                    return a;
                }
            }
            return null;
        }
        /// <summary>
        /// 获得当前标签页的URL
        /// </summary>
        /// <returns></returns>
        public string GetUrl()
        {
            return wb.Address;
        }

        private static Task LoadPageAsync(IWebBrowser browser, string address = null)
        {
            var tcs = new TaskCompletionSource<bool>();

            EventHandler<LoadingStateChangedEventArgs> handler = null;
            handler = (sender, args) =>
            {
                //Wait for while page to finish loading not just the first frame
                if (!args.IsLoading)
                {
                    browser.LoadingStateChanged -= handler;
                    tcs.TrySetResult(true);
                }
            };

            browser.LoadingStateChanged += handler;

            if (!string.IsNullOrEmpty(address))
            {
                browser.Load(address);
            }
            return tcs.Task;
        }
        private static Task LoadHtmlAsync(IWebBrowser browser, string Html, string address = "")
        {
            var tcs = new TaskCompletionSource<bool>();

            EventHandler<LoadingStateChangedEventArgs> handler = null;
            handler = (sender, args) =>
            {
                //Wait for while page to finish loading not just the first frame
                if (!args.IsLoading)
                {
                    browser.LoadingStateChanged -= handler;
                    tcs.TrySetResult(true);
                }
            };

            browser.LoadingStateChanged += handler;

            if (!string.IsNullOrEmpty(address))
            {
                browser.LoadHtml(Html, address);
            }
            return tcs.Task;
        }
        #region WebClient 增强功能
        /// <summary>
        /// WebClient获取 BYTE
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public byte[] DownloadData(string url)
        {
            Task<byte[]> data = nc.Download(url);
            data.Wait();
            return data.Result;
        }
        /// <summary>
        /// WebClient 获取页面源代码
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public string DownloadHtmlCode(string url, Encoding encode = null)
        {
            if (encode == null)
            {
                encode = Encoding.UTF8;
            }
            var bytes = DownloadData(url);
            return encode.GetString(bytes);
        }
        /// <summary>
        /// WebClient Post提交数据获得反馈Byte[]
        /// </summary>
        /// <param name="url">链接</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public byte[] PostDataByte(string url, string value)
        {
            Task<byte[]> data = nc.PostDataByte(url, value);
            data.Wait();
            return data.Result;
        }
        /// <summary>
        /// WebClient Post提交数据获得反馈代码
        /// </summary>
        /// <param name="url">链接</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        public string PostData(string url, string value)
        {
            Task<string> data = nc.PostData(url, value);
            data.Wait();
            return data.Result;
        }
        #endregion
    }
}
